////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __WN_STRINGS_INL__
#define __WN_STRINGS_INL__

#include "WNCore/inc/WNBase.h"
#include "WNCore/inc/WNAssert.h"
#include "WNMemory/inc/WNMemory.h"

#ifdef _WN_WINDOWS
    #include <windows.h>
#endif

#include <string.h>

__WN_INLINE WN_SIZE_T WNStrLen(const WN_CHAR* _string) {
    WN_DEBUG_ASSERT(_string != WN_NULL);

    return(strlen(_string));
}

__WN_INLINE WN_CHAR* WNStrCpy(WN_CHAR* _destination, const WN_CHAR* _source) {
    WN_DEBUG_ASSERT(_destination != WN_NULL);
    WN_DEBUG_ASSERT(_source != WN_NULL);
    WN_DEBUG_ASSERT(_destination != _source);

    return(strcpy(_destination, _source));
}

__WN_INLINE WN_CHAR* WNStrCat(WN_CHAR* _destination, const WN_CHAR* _source) {
    WN_DEBUG_ASSERT(_destination != WN_NULL);
    WN_DEBUG_ASSERT(_source != WN_NULL);
    WN_DEBUG_ASSERT(_destination != _source);

    return(strcat(_destination, _source));
}

__WN_INLINE WN_UINT32 WNStrCmp(const WN_CHAR* _string1, const WN_CHAR* _string2) {
    WN_DEBUG_ASSERT(_string1 != WN_NULL);
    WN_DEBUG_ASSERT(_string2 != WN_NULL);
    WN_DEBUG_ASSERT(_string1 != _string2);

    return(strcmp(_string1, _string2));
}

__WN_INLINE WN_CHAR* WNStrNCpy(WN_CHAR* _destination, const WN_CHAR* _source, WN_SIZE_T _count) {
    WN_DEBUG_ASSERT(_destination != WN_NULL);
    WN_DEBUG_ASSERT(_source != WN_NULL);
    WN_DEBUG_ASSERT(_destination != _source);
    WN_DEBUG_ASSERT(_count != 0);

    return(strncpy(_destination, _source, _count));
}

__WN_INLINE WN_CHAR* WNStrNCat(WN_CHAR* _destination, const WN_CHAR* _source, WN_SIZE_T _count) {
    WN_DEBUG_ASSERT(_destination != WN_NULL);
    WN_DEBUG_ASSERT(_source != WN_NULL);
    WN_DEBUG_ASSERT(_destination != _source);
    WN_DEBUG_ASSERT(_count != 0);

    return(strncat(_destination, _source, _count));
}

__WN_INLINE WN_UINT32 WNStrNCmp(const WN_CHAR* _string1, const WN_CHAR* _string2, WN_SIZE_T _count) {
    WN_DEBUG_ASSERT(_string1 != WN_NULL);
    WN_DEBUG_ASSERT(_string2 != WN_NULL);
    WN_DEBUG_ASSERT(_string1 != _string2);
    WN_DEBUG_ASSERT(_count != 0);

    return(strncmp(_string1, _string2, _count));
}

__WN_INLINE WN_CHAR* WNStrNDup(const WN_CHAR* _string, WN_SIZE_T _length) {
    WN_UINT32 size = 0;
    WN_CHAR* c = WN_NULL;

    WN_DEBUG_ASSERT(_string != NULL);
    WN_DEBUG_ASSERT(_length != 0);

    for (; size < _length && _string[size] != '\0'; size++);

    c = (WN_CHAR*)WNMalloc(size + 1 * sizeof(WN_CHAR));

    if (c != WN_NULL) {
        WNMemCpy(c, _string, size);

        c[size] = '\0';
    }

    return(c);
}

__WN_INLINE WN_CHAR* WNStrChr(WN_CHAR* _string, WN_UINT32 _char) {
    WN_DEBUG_ASSERT(_string != NULL);

    return(strchr(_string, _char));
}

__WN_INLINE const WN_CHAR* WNStrChr(const WN_CHAR* _string, WN_UINT32 _char) {
    WN_DEBUG_ASSERT(_string != NULL);

    return(strchr(_string, _char));
}

__WN_INLINE WN_CHAR* WNStrStr(WN_CHAR* _string1, const WN_CHAR* _string2) {
    WN_DEBUG_ASSERT(_string1 != NULL);
    WN_DEBUG_ASSERT(_string2 != NULL);
    WN_DEBUG_ASSERT(_string1 != _string2);

    return(strstr(_string1, _string2));
}

__WN_INLINE const WN_CHAR* WNStrStr(const WN_CHAR* _string1, const WN_CHAR* _string2) {
    WN_DEBUG_ASSERT(_string1 != NULL);
    WN_DEBUG_ASSERT(_string2 != NULL);
    WN_DEBUG_ASSERT(_string1 != _string2);

    return(strstr(_string1, _string2));
}

__WN_INLINE WN_SIZE_T WNStrHash(const WN_CHAR* _string) {
    WN_SIZE_T hash = 0;
    WN_SIZE_T c;

    WN_DEBUG_ASSERT(_string != NULL);

    while ((c = (WN_SIZE_T)(*(_string++)))) {
        hash = c + (hash << 6) + (hash << 16) - hash;
    }

    return(hash);
}

__WN_INLINE WN_UINT32 WNStrToInt(const WN_CHAR* _string) {
    WN_DEBUG_ASSERT(_string != WN_NULL);

    return(atoi(_string));
}

WN_WCHAR* WNStrToWStr(const WN_CHAR* _string) {
    WN_WCHAR* output = WN_NULL;

    WN_DEBUG_ASSERT(_string != NULL);

    #ifdef _WN_WINDOWS
        const WN_SIZE_T length = WNStrLen(_string);

        output = (WN_WCHAR*)WNMalloc(length * sizeof(WN_WCHAR));

        if (output != WN_NULL) {
            MultiByteToWideChar(CP_ACP, 0, _string, (WN_UINT32)length, output, (WN_UINT32)length);
        }
    #endif

    return(output);
}

WN_CHAR* WNWStrToStr(const WN_WCHAR* _string) {
    WN_CHAR* output = WN_NULL;

    WN_DEBUG_ASSERT(_string != NULL);

    #ifdef _WN_WINDOWS
        const WN_SIZE_T length = WNWStrLen(_string);

        output = (WN_CHAR*)WNMalloc(length * sizeof(WN_CHAR));

        if (output != WN_NULL) {
            WideCharToMultiByte(CP_ACP, 0, _string, (WN_UINT32)length, output, (WN_UINT32)length, WN_NULL, WN_NULL);
        }
    #endif

    return(output);
}

__WN_INLINE WN_SIZE_T WNWStrLen(const WN_WCHAR* _string) {
    WN_DEBUG_ASSERT(_string != NULL);

    return(wcslen(_string));
}

#endif // __WN_STRINGS_INL__